using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace GridSystem
{
    /// <summary>
    /// 【核心组件】将任何GameObject的Transform定义为一个可放置的网格表面。
    /// 完全基于Transform进行坐标变换，实现了【网格朝向自由化】的核心目标。
    /// </summary>
    public class GridSurface : MonoBehaviour
    {
        [Header("配置")]
        [Tooltip("表面的类型标签，用于匹配PlaceableObject的放置规则。")]
        public string surfaceTag = "DefaultFloor";

        [Tooltip("网格单元格在局部坐标系下的大小。")]
        public int cellSize = 1;

        [Tooltip("所有单元格基于此表面的局部Y轴（法线方向）的高度偏移。")]
        public float yOffset = 0f;

        [Header("数据 (由编辑器管理)")]
        [Tooltip("设计师在编辑器中绘制的所有合法单元格的局部坐标。")]
        // 由自定义编辑器接管绘制，不在Inspector中直接显示。
        public List<Vector2Int> definedCells = new();

        // 【运行时数据】用于快速查询单元格状态的字典
        private Dictionary<Vector2Int, Cell> _runtimeGrid;
        public Dictionary<Vector2Int, Cell> RuntimeGrid => _runtimeGrid;

#if UNITY_EDITOR
        [Header("编辑器可视化")]
        [Tooltip("在Scene视图中显示网格可视化。")]
        public bool showVisualization = true;
        [Tooltip("常规填充颜色")]
        public Color fillColor = new(0, 1, 0, 0.25f);
        [Tooltip("常规边框颜色")]
        public Color edgeColor = new(0, 1, 0, 1f);
        [Tooltip("被占用填充颜色")]
        public Color occupiedFillColor = new(1, 0.5f, 0, 0.35f);
        [Tooltip("被占用边框颜色")]
        public Color occupiedEdgeColor = new(1, 0.5f, 0, 1f);
        [Tooltip("边框厚度 (0-0.5)")]
        [Range(0, 0.5f)]
        public float edgeSize = 0.05f;
        [Tooltip("中心锚点的颜色。")]
        public Color anchorColor = Color.yellow;
#endif

        private void Awake()
        {
            InitializeGrid();
        }

#if UNITY_EDITOR

        /// <summary>
        /// 当此组件在场景中被激活时调用。
        /// 主动将自身实例注册到中央登记处。
        /// </summary>
        private void OnEnable()
        {
            GridSystemEvents.ActiveSurfaces.Add(this);
        }

        /// <summary>
        /// 当此组件被禁用或其GameObject被销毁时调用。
        /// 主动将自身实例从中央登记处移除。
        /// </summary>
        private void OnDisable()
        {
            GridSystemEvents.ActiveSurfaces.Remove(this);
        }
# endif

        /// <summary>
        /// 初始化运行时网格字典
        /// </summary>
        public void InitializeGrid()
        {
            _runtimeGrid = new Dictionary<Vector2Int, Cell>();
            foreach (var cellPos in definedCells)
            {
                _runtimeGrid[cellPos] = new Cell(cellPos);
            }
        }

        #region 核心坐标转换方法
        /// <summary>
        /// 将局部网格整数坐标转换为精确的世界坐标。
        /// 这是【网格朝向自由化】的技术基石。
        /// </summary>
        /// <param name="localPosition">局部整数坐标</param>
        /// <returns>对应的世界坐标</returns>
        public Vector3 LocalToWorld(Vector2Int localPosition)
        {
            // 将整数坐标根据cellSize转换为局部浮点坐标，并应用Y轴偏移
            Vector3 localFloatPos = new Vector3(localPosition.x * cellSize, yOffset, localPosition.y * cellSize);
            // 使用TransformPoint将局部坐标转换为世界坐标
            return transform.TransformPoint(localFloatPos);
        }

        /// <summary>
        /// 将世界坐标转换为最近的局部网格整数坐标。
        /// 同样是【网格朝向自由化】的关键实现。
        /// </summary>
        /// <param name="worldPosition">世界坐标</param>
        /// <returns>最近的局部整数坐标</returns>
        public Vector2Int WorldToLocal(Vector3 worldPosition)
        {
            // 使用InverseTransformPoint将世界坐标转换为局部坐标
            Vector3 localPos = transform.InverseTransformPoint(worldPosition);
            // 根据cellSize计算并四舍五入到最近的整数坐标
            // Y轴的偏移不影响XZ平面的计算，所以这里无需改动
            int x = Mathf.RoundToInt(localPos.x / cellSize);
            int z = Mathf.RoundToInt(localPos.z / cellSize);
            return new Vector2Int(x, z);
        }
        #endregion

        #region 表面属性查询
        /// <summary>
        /// 获取表面的世界朝向。
        /// </summary>
        public Quaternion GetSurfaceRotation() => transform.rotation;

        /// <summary>
        /// 获取表面的法线方向（通常是“向上”的方向）。
        /// </summary>
        public Vector3 GetSurfaceNormal() => transform.up;
        #endregion

        #region 单元格状态查询与更新

        /// <summary>
        /// 【运行时】检查一组单元格是否都能合法放置（存在且未被占用）。
        /// </summary>
        /// <param name="cellsToOccupy">需要检查的单元格坐标列表</param>
        /// <returns>是否可以放置</returns>
        public bool CheckPlacementValidity(List<Vector2Int> cellsToOccupy)
        {
            return cellsToOccupy.All(cellPos =>
                _runtimeGrid.ContainsKey(cellPos) && _runtimeGrid[cellPos].State == CellState.Unoccupied);
        }

        /// <summary>
        /// 【运行时】占用一组单元格。
        /// </summary>
        public void OccupyCells(List<Vector2Int> cellsToOccupy, PlaceableObject occupyingObject)
        {
            foreach (Vector2Int cellPos in cellsToOccupy)
            {
                if (_runtimeGrid.ContainsKey(cellPos))
                {
                    _runtimeGrid[cellPos].Occupy(occupyingObject);
                }
            }
        }

        /// <summary>
        /// 【运行时】释放一组单元格。
        /// </summary>
        public void FreeCells(List<Vector2Int> cellsToFree)
        {
            foreach (Vector2Int cellPos in cellsToFree)
            {
                if (_runtimeGrid.ContainsKey(cellPos))
                {
                    _runtimeGrid[cellPos].Free();
                }
            }
        }

        #endregion
    }
}